home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / hplip / scan / sane.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  21.5 KB  |  628 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2008 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Based on:
  20. # "sane.py", part of the Python Imaging Library (PIL)
  21. # http://www.pythonware.com/products/pil/
  22. # Python wrapper on top of the _sane module, which is in turn a very
  23. # thin wrapper on top of the SANE library.  For a complete understanding
  24. # of SANE, consult the documentation at the SANE home page:
  25. # http://www.mostang.com/sane/ .#
  26. #
  27. # Modified to work without PIL by Don Welch
  28. # (C) Copyright 2003 A.M. Kuchling.  All Rights Reserved
  29. # (C) Copyright 2004 A.M. Kuchling, Ralph Heinkel  All Rights Reserved
  30. #
  31. # Permission to use, copy, modify, and distribute this software and its
  32. # documentation for any purpose and without fee is hereby granted,
  33. # provided that the above copyright notice appear in all copies and that
  34. # both that copyright notice and this permission notice appear in
  35. # supporting documentation, and that the name of A.M. Kuchling and
  36. # Ralph Heinkel not be used in advertising or publicity pertaining to 
  37. # distribution of the software without specific, written prior permission.
  38. # A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 
  39. # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  40. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  41. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  42. # USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  43. # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  44. # PERFORMANCE OF THIS SOFTWARE.
  45. # Python wrapper on top of the scanext module, which is in turn a very
  46. # thin wrapper on top of the SANE library.  For a complete understanding
  47. # of SANE, consult the documentation at the SANE home page:
  48. # http://www.mostang.com/sane/ .
  49. #
  50. # Original authors: Andrew Kuchling, Ralph Heinkel
  51. # Modified by: Don Welch
  52. #
  53.  
  54. # Std Lib
  55. import scanext
  56. import threading
  57. import time
  58. import os
  59. import Queue
  60.  
  61. # Local
  62. from base.g import *
  63. from base import utils
  64.  
  65. EVENT_SCAN_CANCELED = 1
  66.  
  67. TYPE_STR = { scanext.TYPE_BOOL:   "TYPE_BOOL",   scanext.TYPE_INT:    "TYPE_INT",
  68.              scanext.TYPE_FIXED:  "TYPE_FIXED",  scanext.TYPE_STRING: "TYPE_STRING",
  69.              scanext.TYPE_BUTTON: "TYPE_BUTTON", scanext.TYPE_GROUP:  "TYPE_GROUP" }
  70.  
  71. UNIT_STR = { scanext.UNIT_NONE:        "UNIT_NONE",
  72.              scanext.UNIT_PIXEL:       "UNIT_PIXEL",
  73.              scanext.UNIT_BIT:         "UNIT_BIT",
  74.              scanext.UNIT_MM:          "UNIT_MM",
  75.              scanext.UNIT_DPI:         "UNIT_DPI",
  76.              scanext.UNIT_PERCENT:     "UNIT_PERCENT",
  77.              scanext.UNIT_MICROSECOND: "UNIT_MICROSECOND" }
  78.  
  79.  
  80. class Option:
  81.     """Class representing a SANE option.
  82.     Attributes:
  83.     index -- number from 0 to n, giving the option number
  84.     name -- a string uniquely identifying the option
  85.     title -- single-line string containing a title for the option
  86.     desc -- a long string describing the option; useful as a help message
  87.     type -- type of this option.  Possible values: TYPE_BOOL,
  88.             TYPE_INT, TYPE_STRING, and so forth.
  89.     unit -- units of this option.  Possible values: UNIT_NONE,
  90.             UNIT_PIXEL, etc.
  91.     size -- size of the value in bytes
  92.     cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc.
  93.     constraint -- constraint on values.  Possible values:
  94.         None : No constraint
  95.         (min,max,step)  Integer values, from min to max, stepping by
  96.         list of integers or strings: only the listed values are allowed
  97.     """
  98.  
  99.     def __init__(self, args, cur_device):
  100.         import string
  101.         self.cur_device = cur_device
  102.         
  103.         self.index, self.name, self.title, self.desc, self.type, \
  104.             self.unit, self.size, self.cap, self.constraint = args
  105.         
  106.         if type(self.name) != type(''): 
  107.             self.name = str(self.name)
  108.             
  109.     def isActive(self):
  110.         return scanext.isOptionActive(self.cap)
  111.         
  112.     def isSettable(self):
  113.         return scanext.isOptionSettable(self.cap)
  114.         
  115.     def __repr__(self):
  116.         if self.isSettable():
  117.             settable = 'yes'
  118.         else:
  119.             settable = 'no'
  120.         
  121.         if self.isActive():
  122.             active = 'yes'
  123.             curValue = self.cur_device.getOption(self.name)
  124.         else:
  125.             active = 'no'
  126.             curValue = '<not available, inactive option>'
  127.             
  128.         
  129.         return """\nName:      %s
  130. Cur value: %s
  131. Index:     %d
  132. Title:     %s
  133. Desc:      %s
  134. Type:      %s
  135. Unit:      %s
  136. Constr:    %s
  137. isActive:    %s
  138. isSettable:  %s\n""" % (self.name, curValue,
  139.                       self.index, self.title, self.desc,
  140.                       TYPE_STR[self.type], UNIT_STR[self.unit],
  141.                       self.constraint, active, settable)
  142.         return s
  143.         
  144.     def limitAndSet(self, value):
  145.         if value is not None and self.constraint is not None:
  146.             if type(self.constraint) == type(()):
  147.                 if value < self.constraint[0]:
  148.                     value = self.constraint[0]
  149.                     log.warn("Invalid value for %s (%s < min value of %d). Using %d." % 
  150.                         (self.name, self.name, value, value))
  151.                 
  152.                 elif value > self.constraint[1]:
  153.                     value = self.constraint[1]
  154.                     log.warn("Invalid value for %s (%s > max value of %d). Using %d." % 
  155.                         (self.name, self.name, value, value))
  156.                     
  157.                 self.cur_device.setOption(self.name, value)
  158.             
  159.             elif type(self.constraint) == type([]):
  160.                 if value not in self.constraint:
  161.                     v = self.constraint[0]
  162.                     min_dist = sys.maxint
  163.                     for x in self.constraint: 
  164.                         if abs(value-x) < min_dist:
  165.                             min_dist = abs(value-x)
  166.                             v = x
  167.                             
  168.                     log.warn("Invalid value for %s (%s not in constraint list: %s). Using %d." % 
  169.                         (self.name, self.name, value, ', '.join(self.constraint), v))
  170.                         
  171.                     self.cur_device.setOption(self.name, v)
  172.         
  173.         else:
  174.             value = self.cur_device.getOption(self.name)
  175.             
  176.         return value
  177.  
  178.  
  179. ##class _SaneIterator:
  180. ##    """ intended for ADF scans.
  181. ##    """
  182. ##
  183. ##    def __init__(self, cur_device):
  184. ##        self.cur_device = cur_device
  185. ##
  186. ##    def __iter__(self):
  187. ##        return self
  188. ##
  189. ##    def __del__(self):
  190. ##        self.cur_device.cancelScan()
  191. ##
  192. ##    def next(self):
  193. ##        try:
  194. ##            self.cur_device.startScan()
  195. ##        except error, v:
  196. ##            if v == 'Document feeder out of documents':
  197. ##                raise StopIteration
  198. ##            else:
  199. ##                raise
  200. ##        return self.cur_device.performScan(1)
  201.         
  202.  
  203.  
  204.  
  205. class ScanDevice:
  206.     """Class representing a SANE device.
  207.     Methods:
  208.     startScan()    -- initiate a scan, using the current settings
  209.     cancelScan()   -- cancel an in-progress scanning operation
  210.  
  211.     Also available, but rather low-level:
  212.     getParameters() -- get the current parameter settings of the device
  213.     getOptions()    -- return a list of tuples describing all the options.
  214.  
  215.     Attributes:
  216.     optlist -- list of option names
  217.  
  218.     You can also access an option name to retrieve its value, and to
  219.     set it.  For example, if one option has a .name attribute of
  220.     imagemode, and scanner is a ScanDevice object, you can do:
  221.          print scanner.imagemode
  222.          scanner.imagemode = 'Full frame'
  223.          scanner.['imagemode'] returns the corresponding Option object.
  224.     """
  225.     
  226.     def __init__(self, dev):
  227.         self.scan_thread = None
  228.         self.dev = scanext.openDevice(dev)
  229.         self.options = {}
  230.         self.__load_options_dict()
  231.  
  232.  
  233.     def __load_options_dict(self):
  234.         opts = self.options
  235.         opt_list = self.dev.getOptions()
  236.         
  237.         for t in opt_list:
  238.             o = Option(t, self)
  239.             
  240.             if o.type != scanext.TYPE_GROUP:
  241.                 opts[o.name] = o
  242.  
  243.  
  244.     def setOption(self, key, value):
  245.         opts = self.options
  246.         
  247.         if key not in opts:
  248.             opts[key] = value
  249.             return
  250.         
  251.         opt = opts[key]
  252.         
  253.         if opt.type == scanext.TYPE_GROUP:
  254.             log.error("Groups can't be set: %s" % key)
  255.         
  256.         if not scanext.isOptionActive(opt.cap):
  257.             log.error("Inactive option: %s" % key)
  258.         
  259.         if not scanext.isOptionSettable(opt.cap):
  260.             log.error("Option can't be set by software: %s" % key)
  261.         
  262.         if type(value) == int and opt.type == scanext.TYPE_FIXED:
  263.             # avoid annoying errors of backend if int is given instead float:
  264.             value = float(value)
  265.         
  266.         try:
  267.             self.last_opt = self.dev.setOption(opt.index, value)
  268.         except scanext.error:
  269.             log.error("Unable to set option %s to value %s" % (key, value))
  270.         
  271.         # do binary AND to find if we have to reload options:
  272.         if self.last_opt & scanext.INFO_RELOAD_OPTIONS:
  273.             self.__load_options_dict()
  274.             
  275.  
  276.     def getOption(self, key):
  277.         opts = self.options
  278.         
  279.         if key == 'optlist':
  280.             return opts.keys()
  281.         
  282.         if key == 'area':
  283.             return (opts["tl-x"], opts["tl-y"]), (opts["br-x"], opts["br-y"])
  284.         
  285.         if key not in opts:
  286.             raise AttributeError, 'No such attribute: %s' % key
  287.         
  288.         opt = opts[key]
  289.         
  290.         if opt.type == scanext.TYPE_BUTTON:
  291.             raise AttributeError, "Buttons don't have values: %s" % key
  292.         
  293.         if opt.type == scanext.TYPE_GROUP:
  294.             raise AttributeError, "Groups don't have values: %s " % key
  295.         
  296.         if not scanext.isOptionActive(opt.cap):
  297.             raise AttributeError, 'Inactive option: %s' % key
  298.         
  299.         return self.dev.getOption(opt.index)
  300.  
  301.  
  302.     def getOptionObj(self, key):
  303.         opts = self.options
  304.         if key in opts:
  305.             return opts[key]
  306.         
  307.  
  308.     def getParameters(self):
  309.         """Return a 6-tuple holding all the current device settings:
  310.            (format, format_name, last_frame, (pixels_per_line, lines), depth, bytes_per_line)
  311.         
  312.             - format is the SANE frame type
  313.             - format is one of 'grey', 'color' (RGB), 'red', 'green', 'blue'.
  314.             - last_frame [bool] indicates if this is the last frame of a multi frame image
  315.             - (pixels_per_line, lines) specifies the size of the scanned image (x,y)
  316.             - lines denotes the number of scanlines per frame
  317.             - depth gives number of pixels per sample
  318.         """
  319.         return self.dev.getParameters()
  320.  
  321.  
  322.     def getOptions(self):
  323.         "Return a list of tuples describing all the available options"
  324.         return self.dev.getOptions()
  325.         
  326.         
  327.     def startScan(self, byte_format='BGRA', update_queue=None, event_queue=None):
  328.         """
  329.             Perform a scan with the current device.
  330.             Calls sane_start().
  331.         """
  332.         if not self.isScanActive():
  333.             status = self.dev.startScan()
  334.             self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  335.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  336.             
  337.             self.scan_thread = ScanThread(self.dev, byte_format, update_queue, event_queue)
  338.             self.scan_thread.scan_active = True
  339.             self.scan_thread.start()
  340.             return True, self.lines * self.bytes_per_line, status
  341.         else:
  342.             # Already active
  343.             return False, 0, scanext.SANE_STATUS_DEVICE_BUSY
  344.             
  345.             
  346.     def cancelScan(self):
  347.         "Cancel an in-progress scanning operation."
  348.         return self.dev.cancelScan()
  349.  
  350.  
  351.     def getScan(self):
  352.         "Get the output buffer and info about a completed scan."
  353.         if not self.isScanActive():
  354.             s = self.scan_thread
  355.             
  356.             return s.buffer, s.format, s.format_name, s.pixels_per_line, \
  357.                 s.lines, s.depth, s.bytes_per_line, s.pad_bytes, s.total_read
  358.                 
  359.                 
  360.     def freeScan(self):
  361.         "Cleanup the scan file after a completed scan."
  362.         if not self.isScanActive():
  363.             s = self.scan_thread
  364.             
  365.             try:
  366.                 s.buffer.close()
  367.                 os.remove(s.buffer_path)
  368.             except (IOError, AttributeError):
  369.                 pass
  370.         
  371.         
  372.     def isScanActive(self):
  373.         if self.scan_thread is not None:
  374.             return self.scan_thread.isAlive() and self.scan_thread.scan_active
  375.         else:
  376.             return False
  377.             
  378.             
  379.     def waitForScanDone(self):
  380.         if self.scan_thread is not None and \
  381.             self.scan_thread.isAlive() and \
  382.             self.scan_thread.scan_active:
  383.  
  384.             try:
  385.                 self.scan_thread.join()
  386.             except KeyboardInterrupt:
  387.                 pass
  388.                 
  389.                 
  390.     def waitForScanActive(self):
  391.         time.sleep(0.5)
  392.         if self.scan_thread is not None:
  393.             while True:
  394.                 #print self.scan_thread.isAlive()
  395.                 #print self.scan_thread.scan_active
  396.                 if self.scan_thread.isAlive() and \
  397.                     self.scan_thread.scan_active:
  398.                     return
  399.                 
  400.                 time.sleep(0.5)
  401.                 #print "Waiting..."
  402.         
  403.         
  404. ##    def scanMulti(self):
  405. ##        return _SaneIterator(self)
  406.         
  407.         
  408.     def closeScan(self):
  409.         "Close the SANE device after a scan."
  410.         self.dev.closeScan()
  411.  
  412.         
  413.         
  414. class ScanThread(threading.Thread):
  415.     def __init__(self, device, byte_format='BGRA', update_queue=None, event_queue=None):
  416.         threading.Thread.__init__(self)
  417.         self.scan_active = True
  418.         self.dev = device
  419.         self.update_queue = update_queue
  420.         self.event_queue = event_queue
  421.         self.buffer_fd, self.buffer_path = utils.make_temp_file(prefix='hpscan')
  422.         self.buffer = os.fdopen(self.buffer_fd, "w+b")
  423.         self.format = -1
  424.         self.format_name = ''
  425.         self.last_frame = -1
  426.         self.pixels_per_line = -1
  427.         self.lines = -1
  428.         self.depth = -1
  429.         self.bytes_per_line = -1
  430.         self.pad_bytes = -1
  431.         self.total_read = 0
  432.         self.byte_format = byte_format
  433.         
  434.         
  435.     def updateQueue(self, status, bytes_read):
  436.         if self.update_queue is not None:
  437.             try:
  438.                 status = int(status)
  439.             except (ValueError, TypeError):
  440.                 status = -1 #scanext.SANE_STATUS_GOOD
  441.                 
  442.             self.update_queue.put((status, bytes_read))
  443.             time.sleep(0)
  444.  
  445.  
  446.     def run(self):
  447.         #self.scan_active = True
  448.         self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  449.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  450.             
  451.         log.debug("format=%d" % self.format)
  452.         log.debug("format_name=%s" % self.format_name)
  453.         log.debug("last_frame=%d" % self.last_frame)
  454.         log.debug("ppl=%d" % self.pixels_per_line)
  455.         log.debug("lines=%d" % self.lines)
  456.         log.debug("depth=%d" % self.depth)
  457.         log.debug("bpl=%d" % self.bytes_per_line)
  458.         log.debug("byte_format=%s" % self.byte_format)
  459.             
  460.         w = self.buffer.write
  461.         
  462.         if self.format == scanext.FRAME_RGB: # "Color"
  463.             if self.depth == 8: # 8 bpp (32bit)
  464.                 self.pad_bytes = self.bytes_per_line - 3 * self.pixels_per_line
  465.                 
  466.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  467.                 
  468.                 dir = -1
  469.                 if self.byte_format == 'RGBA':
  470.                     dir = 1
  471.                 
  472.                 try:
  473.                     st, t = self.dev.readScan(self.bytes_per_line)
  474.                 except scanext.error, st:
  475.                     self.updateQueue(st, 0)
  476.                 
  477.                 #print st
  478.                 while st == scanext.SANE_STATUS_GOOD:
  479.                     
  480.                     if t:
  481.                         index = 0
  482.                         while index < len(t) - self.pad_bytes:
  483.                             w(t[index:index+3:dir])
  484.                             w('\xff')
  485.                             index += 3
  486.             
  487.                         self.total_read += len(t)
  488.                         self.updateQueue(st, self.total_read)
  489.                         log.debug("Read %d bytes" % self.total_read)
  490.                     
  491.                     else:
  492.                         time.sleep(0.1)
  493.                     
  494.                     try:
  495.                         st, t = self.dev.readScan(self.bytes_per_line)
  496.                     except scanext.error, st:
  497.                         self.updateQueue(st, self.total_read)
  498.                         break
  499.                     
  500.                     if self.checkCancel():
  501.                         break
  502.         
  503.         elif self.format == scanext.FRAME_GRAY: 
  504.         
  505.             if self.depth == 1: # 1 bpp lineart
  506.                 self.pad_bytes = self.bytes_per_line - (self.pixels_per_line + 7) // 8;
  507.                 
  508.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  509.                 
  510.                 try:
  511.                     st, t = self.dev.readScan(self.bytes_per_line)
  512.                 except scanext.error, st:
  513.                     self.updateQueue(st, 0)
  514.                
  515.                 while st == scanext.SANE_STATUS_GOOD:
  516.                     
  517.                     if t:
  518.                         index = 0
  519.                         while index < len(t) - self.pad_bytes:
  520.                             k = 0x80
  521.                             j = ord(t[index])
  522.                             
  523.                             for b in range(8):
  524.                                 if k & j:
  525.                                     w("\x00\x00\x00\xff")
  526.                                 else:
  527.                                     w("\xff\xff\xff\xff")
  528.                                 
  529.                                 k = k >> 1
  530.                             
  531.                             index += 1
  532.                     
  533.                         self.total_read += len(t)
  534.                         self.updateQueue(st, self.total_read)
  535.                         log.debug("Read %d bytes" % self.total_read)
  536.                     else:
  537.                         time.sleep(0.1)
  538.                         
  539.                     try:
  540.                         st, t = self.dev.readScan(self.bytes_per_line)
  541.                     except scanext.error, st:
  542.                         self.updateQueue(st, self.total_read)
  543.                         break
  544.                 
  545.                     if self.checkCancel():
  546.                         break
  547.                     
  548.             elif self.depth == 8: # 8 bpp grayscale
  549.                 self.pad_bytes = self.bytes_per_line - self.pixels_per_line
  550.                 
  551.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  552.                 
  553.                 try:
  554.                     st, t = self.dev.readScan(self.bytes_per_line)
  555.                 except scanext.error, st:
  556.                     self.updateQueue(st, 0)
  557.                 
  558.                 while st == scanext.SANE_STATUS_GOOD:
  559.                     
  560.                     if t:
  561.                         index = 0
  562.                         while index < len(t) - self.pad_bytes:
  563.                             j = t[index]
  564.                             w(j)
  565.                             w(j)
  566.                             w(j)
  567.                             w("\xff")
  568.                             
  569.                             index += 1
  570.                     
  571.                         self.total_read += len(t)
  572.                         self.updateQueue(st, self.total_read)
  573.                         log.debug("Read %d bytes" % self.total_read)
  574.                     else:
  575.                         time.sleep(0.1)
  576.                         
  577.                     try:
  578.                         st, t = self.dev.readScan(self.bytes_per_line)
  579.                     except scanext.error, st:
  580.                         self.updateQueue(st, self.total_read)
  581.                         break
  582.                     
  583.                     if self.checkCancel():
  584.                         break
  585.                     
  586.         #self.dev.cancelScan()
  587.         self.buffer.seek(0)
  588.         self.scan_active = False
  589.         log.debug("Scan thread exiting...")
  590.         
  591.         
  592.     def checkCancel(self):
  593.         canceled = False
  594.         while self.event_queue.qsize():
  595.             try:
  596.                 event = self.event_queue.get(0)
  597.                 if event == EVENT_SCAN_CANCELED:
  598.                     canceled = True
  599.                     log.debug("Cancel pressed!")
  600.                     self.dev.cancelScan()
  601.             
  602.             except Queue.Empty:
  603.                 break
  604.  
  605.         return canceled
  606.         
  607.  
  608.  
  609. def init():
  610.     return scanext.init()
  611.     
  612.     
  613. def deInit():
  614.     return scanext.deInit()
  615.     
  616.     
  617. def openDevice(dev):
  618.     "Open a device for scanning"
  619.     return ScanDevice(dev)
  620.     
  621.     
  622. def getDevices(local_only=0):
  623.     return scanext.getDevices(local_only)
  624.     
  625.     
  626.